/**
*********************************************************************************************************
*               Copyright(c) 2018, Realtek Semiconductor Corporation. All rights reserved.
*********************************************************************************************************
* @file     dataTrans_uart.c
* @brief    This file provides uart transport layer driver.
* @details
* @author   elliot_chen
* @date     2018-11-16
* @version  v1.0
*********************************************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
///#define LOG_TAG    "TRANSUART"
#include "asr_rtl8763bo_port.h"

#if USE_LV_BLUETOOTH != 0

#include "dataTrans_uart.h"
#include "dataTrans_queue.h"
#include "rtl8763bf_task.h"
#include "dataTrans_hci.h"
#include "hci_cmd.h"
#include "bt_api.h"

/* Internal defines ----------------------------------------------------------*/
#define         UART_RX_TRIGGER_VALUE           14
#define swap32(x)  ((((x)>>24)&0xff)|(((x)<<8)&0xff0000)|(((x)>>8)&0xff00)|(((x)<<24)&0xff000000))

/* Globals -------------------------------------------------------------------*/

/* Function declaration ------------------------------------------------------*/

int DataTrans_UARTIntrHandler(uint32 port_id);

extern void bt_uart_irq_init(bt_uart_cfg * port);
extern void bt_uart_basic_init(bt_uart_cfg * port);
extern BOOL IsChipCraneG_AX(void);
extern BOOL IsChipCraneM(void);

typedef int (*rx_dma_cb_t)(void * peri, uint16_t len);
extern void bt_uart_register_rx_dma_cb(rx_dma_cb_t rx_cb);


/* don't defined in function: DMA HISR will be stack overflow */
static uint8_t DataPacketBuf[256];


/*static*/ bt_uart_cfg rtlbt_uart_port = {
    1,                  //you can change it, if you use other UART port
    1,                  //do not change it
    0,                  //do not change it
    0,                  //do not change it
    bt_rx_int,          //do not change it
    0,                  //do not change it
    0,                  //do not change it
    BAUDRATE_115200,    //do not change it. it is a default baudrate. stack will change baudrate accroding bt_user_cfg
    BT_DATA_8,          //do not change it
    BT_PARITY_NONE,     //do not change it
    NULL,               //do not change it. host stack will use it
    NULL,               //do not change it. host stack will use it
};

/*static*/ void init_bt_uart(void)
{
    if(IsChipCraneG_AX()) {
        MODLOGD("chip is craneg, change port id to UART4_ID");
        rtlbt_uart_port.port_id = UART4_ID;
    }

    if(rtlbt_uart_port.port_id == 1) { // UART port 1
        unsigned int data;
        // config uart gpio
        data = (*(volatile unsigned int *)(0xd401e1a8));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1a8, data);
        data = (*(volatile unsigned int *)(0xd401e1ac));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1ac, data);
        // cts
        data = (*(volatile unsigned int *)(0xd401e1b0));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1b0, data);
        // rts
        data = (*(volatile unsigned int *)(0xd401e1b4));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1b4, data);
    } else if(rtlbt_uart_port.port_id == UART4_ID && IsChipCraneG_AX()) { // UART port 4
        unsigned int data;
        // config uart gpio
        data = (*(volatile unsigned int *)(0xd401e1a8));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1e8, data);
        data = (*(volatile unsigned int *)(0xd401e1ac));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1ec, data);
        // cts
        data = (*(volatile unsigned int *)(0xd401e1b0));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1f0, data);
        // rts
        data = (*(volatile unsigned int *)(0xd401e1b4));
        data = data & ~(0x7) | 0x1;
        write32(0xd401e1f4, data);
    } else if(rtlbt_uart_port.port_id == UART4_ID && IsChipCraneM()) { // UART port 4
        unsigned int data;
        unsigned int reg;
        // config uart gpio
        reg = 0xd401e000 + 4 * 55;
        data = (*(volatile unsigned int *)(reg));
        data = data & ~(0x7) | 0x7;
        write32(reg, data);
        MODLOGD("register %08X-%08x\r\n", reg, *(volatile unsigned int *)(reg));

        reg = 0xd401e000 + 4 * 56;
        data = (*(volatile unsigned int *)(reg));
        data = data & ~(0x7) | 0x7;
        write32(reg, data);
        MODLOGD("register %08X-%08x\r\n", reg, *(volatile unsigned int *)(reg));
        // cts
        reg = 0xd401e000 + 4 * 57;
        data = (*(volatile unsigned int *)(reg));
        data = data & ~(0x7) | 0x7;
        write32(reg, data);
        MODLOGD("register %08X-%08x\r\n", reg, *(volatile unsigned int *)(reg));
        // rts
        reg = 0xd401e000 + 4 * 58;
        data = (*(volatile unsigned int *)(reg));
        data = data & ~(0x7) | 0x7;
        write32(reg, data);
        MODLOGD("register %08X-%08x\r\n", reg, *(volatile unsigned int *)(reg));
    }
}
#ifndef DATATRANS_UART_DMA
static uint16_t UART_ReceiveData(void * peri, void * pbuf, uint16_t len)
{
    return bt_uart_recv_data(rtlbt_uart_port.port_id, (uint8 *)pbuf, len);
}

int DataTrans_UARTRxFifoIntrCallback(uint32 port_id)
{
    UI_EVENT ev = {0};
    DataTrans_WriteQueue(NULL, UART_RX_TRIGGER_VALUE);

    memset((VOID *)&ev, 0x0, sizeof(UI_EVENT));
    ev.nEventId = IO_MSG_UART_RX_PKT;
    if(uos_send_event(bbpro_hci_receive_task_handle, &ev, UOS_WAIT_FOREVER, UOS_EVENT_PRI_NORMAL) != true) {
        APP_PRINT_ERROR0("[talkband]bbppro hci receive queue send fail");
    }

    return 0; // TODO:
}
#endif
static uint16_t UART_CopyData(void * peri, void * pbuf, uint16_t len)
{
    memcpy(pbuf, peri, len);

    return len;
}

// version 2, better
int DataTrans_UARTRxDmaIntrCallback(void * peri, uint16_t len)
{
    UI_EVENT ev = {0};
#ifdef SEND_EVENT_TO_RECV_TASK_WITH_PARAM
    Rx_PackDebugTypeDef * event;
#endif
    uint8_t sync_word;
    uint8_t * head = (uint8_t *)peri;
    uint16_t length, opcode, curr_index, copyed;
    static uint16_t buf_len = 0, total_packet_len = 0;
    static bool bNeedAssemble = false;
    uint8_t dummy_checksum = 0x55;

    QUEUE_WRITE_STAT ret;
    static uint8_t overflow_warnings = 0;
#define QUEUE_RESET_THRESHHOLD 3

    SetBTPmLock(BT_WAKELOCK_UART_RECV | BT_WAKELOCK_UART_TX_DMA);

    sync_word = head[0];
    length = ((head[3] << 8) | head[2]);
    opcode = ((head[5] << 8) | head[4]);

    if(bNeedAssemble &&
            (sync_word == HCI_SYNC_WORD) && (opcode == HCI_CMD_AUDIO_DATA)) {
        ///MODLOGE("DMA lost packet");
        bNeedAssemble = false; // reset to new packet

        if(buf_len >= HCI_PACK_MIN_WIDTH) {
            DataPacketBuf[2] = (buf_len - HCI_PACK_CTRL_FILED_WIDTH) & 0xff;
            DataPacketBuf[3] = ((buf_len - HCI_PACK_CTRL_FILED_WIDTH) << 8) & 0xff;

            uart_rx_lock();
            ret = DataTrans_WriteQueue(&DataPacketBuf[0], buf_len);
            if(ret == QUEUE_WRITE_OK) {
                ret = DataTrans_WriteQueue(&dummy_checksum, 1);
            }
            uart_rx_unlock();

            if(ret == QUEUE_WRITE_OVERFLOW) {
                overflow_warnings ++;
                if(overflow_warnings == QUEUE_RESET_THRESHHOLD) {
                    overflow_warnings = 0;
                    uart_rx_lock();
                    DataTrans_InitQueue();
                    ret = DataTrans_WriteQueue(&DataPacketBuf[0], buf_len);
                    ret = DataTrans_WriteQueue(&dummy_checksum, 1);
                    uart_rx_unlock();
                }
            } else if(ret == QUEUE_WRITE_OK) {
                overflow_warnings = 0;
            }

            memset((VOID *)&ev, 0x0, sizeof(UI_EVENT));
#ifdef SEND_EVENT_TO_RECV_TASK_WITH_PARAM
            event = (Rx_PackDebugTypeDef *)malloc(sizeof(Rx_PackDebugTypeDef));
            if(event == NULL) {
                return ;
            }
            memset((void *)event, 0, sizeof(Rx_PackDebugTypeDef));
            event->packet_type = PACKET_TYPE_BROKEN;
            event->param1 = swap32(*(uint32_t *)peri);
            event->param2 = len;
            event->param3 = swap32(*(uint32_t *)&DataPacketBuf[0]);
            event->param4 = buf_len;
            ev.nParam1 = (uint32_t)event;  /// the assembled packet
#endif
            ev.nEventId = IO_MSG_UART_RX_PKT;
            uos_send_event(bbpro_hci_receive_task_handle, &ev, UOS_NO_WAIT, UOS_EVENT_PRI_URGENT);
        }
    }

    if(bNeedAssemble ||
            ((sync_word == HCI_SYNC_WORD) && (opcode == HCI_CMD_AUDIO_DATA) && (len < (length + HCI_HEADER_AND_CHECK_SUM_LEN)))) {
        if(bNeedAssemble == false) {
            buf_len = 0;
            curr_index = 0;
            bNeedAssemble = true;
            total_packet_len = length + HCI_HEADER_AND_CHECK_SUM_LEN;
            memset(&DataPacketBuf[0], 0, sizeof(DataPacketBuf));
        }

        copyed = buf_len + len <= 255 ? len : 255 - buf_len;

        curr_index = buf_len;

        memcpy(&DataPacketBuf[curr_index], peri, copyed);

        buf_len += copyed;

        if(buf_len < total_packet_len) {
#if 0
            memset((VOID *)&ev, 0x0, sizeof(UI_EVENT));
#ifdef SEND_EVENT_TO_RECV_TASK_WITH_PARAM
            event = (Rx_PackDebugTypeDef *)malloc(sizeof(Rx_PackDebugTypeDef));
            if(event == NULL) {
                return ;
            }
            memset((void *)event, 0, sizeof(Rx_PackDebugTypeDef));
            event->packet_type = PACKET_TYPE_ASSEMBLED;
            event->param1 = swap32(*(uint32_t *)peri);
            event->param2 = len;
            event->param3 = swap32(*(uint32_t *)&DataPacketBuf[curr_index]);
            event->param4 = buf_len;
            ev.nParam1 = (uint32_t)event;  /// the assembled packet
#endif
            ev.nEventId = IO_MSG_UART_RX_PKT;
            uos_send_event(bbpro_hci_receive_task_handle, &ev, UOS_NO_WAIT, UOS_EVENT_PRI_URGENT);
#endif
        } else {
            bNeedAssemble = false;

            if(buf_len > total_packet_len)
                buf_len = total_packet_len;

            uart_rx_lock();
            ret = DataTrans_WriteQueue(&DataPacketBuf[0], buf_len);
            uart_rx_unlock();

            if(ret == QUEUE_WRITE_OVERFLOW) {
                overflow_warnings ++;
                if(overflow_warnings == QUEUE_RESET_THRESHHOLD) {
                    overflow_warnings = 0;
                    uart_rx_lock();
                    DataTrans_InitQueue();
                    ret = DataTrans_WriteQueue(&DataPacketBuf[0], buf_len);
                    uart_rx_unlock();
                }
            } else if(ret == QUEUE_WRITE_OK) {
                overflow_warnings = 0;
            }

            memset((VOID *)&ev, 0x0, sizeof(UI_EVENT));
#ifdef SEND_EVENT_TO_RECV_TASK_WITH_PARAM
            event = (Rx_PackDebugTypeDef *)malloc(sizeof(Rx_PackDebugTypeDef));
            if(event == NULL) {
                return ;
            }
            memset((void *)event, 0, sizeof(Rx_PackDebugTypeDef));
            event->packet_type = PACKET_TYPE_LAST;
            event->param1 = swap32(*(uint32_t *)peri);
            event->param2 = len;
            event->param3 = swap32(*(uint32_t *)&DataPacketBuf[curr_index]);
            event->param4 = buf_len;
            ev.nParam1 = (uint32_t)event;  /// the assembled packet
#endif
            ev.nEventId = IO_MSG_UART_RX_PKT;
            uos_send_event(bbpro_hci_receive_task_handle, &ev, UOS_NO_WAIT, UOS_EVENT_PRI_URGENT);
        }
    } else {
        if((opcode == HCI_CMD_AUDIO_DATA) && (len > (length + HCI_HEADER_AND_CHECK_SUM_LEN)))
            len = length + HCI_HEADER_AND_CHECK_SUM_LEN;

        uart_rx_lock();
        ret = DataTrans_WriteQueue(peri, len);
        uart_rx_unlock();

        if(ret == QUEUE_WRITE_OVERFLOW) {
            overflow_warnings ++;
            if(overflow_warnings == QUEUE_RESET_THRESHHOLD) {
                overflow_warnings = 0;
                uart_rx_lock();
                DataTrans_InitQueue();
                ret = DataTrans_WriteQueue(peri, len);
                uart_rx_unlock();
            }
        } else if(ret == QUEUE_WRITE_OK) {
            overflow_warnings = 0;
        }

        memset((VOID *)&ev, 0x0, sizeof(UI_EVENT));
#ifdef SEND_EVENT_TO_RECV_TASK_WITH_PARAM
        event = (Rx_PackDebugTypeDef *)malloc(sizeof(Rx_PackDebugTypeDef));
        if(event == NULL) {
            return ;
        }
        memset((void *)event, 0, sizeof(Rx_PackDebugTypeDef));
        event->packet_type = PACKET_TYPE_ENTIRED;
        event->param1 = swap32(*(uint32_t *)peri);
        event->param2 = len;
        ev.nParam1 = (uint32_t)event;  /// the whole packet
#endif
        ev.nEventId = IO_MSG_UART_RX_PKT;
        if(uos_send_event(bbpro_hci_receive_task_handle, &ev, UOS_NO_WAIT, UOS_EVENT_PRI_URGENT) != true) {
            APP_PRINT_ERROR0("[talkband]bbppro hci receive queue send fail");
        }
    }

    return len;
}

// version 3, write audio data to g_pcm_rx_rbuf directly
#if 0
extern rbuf_t * g_pcm_rx_rbuf;

void DataTrans_UARTRxDmaIntrCallback(void * peri, uint16_t len)
{
    UI_EVENT ev = {0};
    Rx_PackDebugTypeDef * event;
    uint8_t sync_word;
    uint8_t * head = (uint8_t *)peri;
    uint16_t length, opcode, curr_index, copyed;
    static uint16_t buf_len = 0, total_packet_len = 0;
    static bool bNeedAssemble = false;
    uint32_t lcr = 0, lsr = 0;
    uint8_t dummy_checksum = 0x55;

    QUEUE_WRITE_STAT ret;
    static uint8_t overflow_warnings = 0;
#define QUEUE_RESET_THRESHHOLD 3

    SetBTPmLock(BT_WAKELOCK_UART_RECV | BT_WAKELOCK_UART_TX_DMA);

    sync_word = head[0];
    length = ((head[3] << 8) | head[2]);
    opcode = ((head[5] << 8) | head[4]);

    if(bNeedAssemble &&
            (sync_word == HCI_SYNC_WORD) && (opcode == HCI_CMD_AUDIO_DATA)) {
        ///MODLOGE("DMA lost packet");
        bNeedAssemble = false; // reset to new packet
    }

    if(bNeedAssemble ||
            ((sync_word == HCI_SYNC_WORD) && (opcode == HCI_CMD_AUDIO_DATA) && (len < (length + HCI_HEADER_AND_CHECK_SUM_LEN)))) {
        if(bNeedAssemble == false) {
            buf_len = 0;
            curr_index = 0;
            bNeedAssemble = true;
            total_packet_len = length + HCI_HEADER_AND_CHECK_SUM_LEN;
            memset(&DataPacketBuf[0], 0, sizeof(DataPacketBuf));
        }

        copyed = buf_len + len <= 255 ? len : 255 - buf_len;

        curr_index = buf_len;

        memcpy(&DataPacketBuf[curr_index], peri, copyed);

        buf_len += copyed;

        if(buf_len < total_packet_len) {

        } else {
            bNeedAssemble = false;

            if(buf_len > total_packet_len)
                buf_len = total_packet_len;

            bbpro_rbuf_write(g_pcm_rx_rbuf, &DataPacketBuf[HCI_HEADER_AND_OPCODE_LEN + HCI_AUDIO_DATA_SEQN_WIDTH], HCI_AUDIO_DATA_WIDTH);
        }
    } else {
        if((sync_word == HCI_SYNC_WORD) && (opcode == HCI_CMD_AUDIO_DATA) && (len >= (length + HCI_HEADER_AND_CHECK_SUM_LEN))) {
            bbpro_rbuf_write(g_pcm_rx_rbuf, &head[HCI_HEADER_AND_OPCODE_LEN + HCI_AUDIO_DATA_SEQN_WIDTH], HCI_AUDIO_DATA_WIDTH);
        } else {
            uart_rx_lock();
            ret = DataTrans_WriteQueue(peri, len);
            uart_rx_unlock();

            if(ret == QUEUE_WRITE_OVERFLOW) {
                overflow_warnings ++;
                if(overflow_warnings == QUEUE_RESET_THRESHHOLD) {
                    overflow_warnings = 0;
                    uart_rx_lock();
                    DataTrans_InitQueue();
                    ret = DataTrans_WriteQueue(peri, len);
                    uart_rx_unlock();
                }
            } else if(ret == QUEUE_WRITE_OK) {
                overflow_warnings = 0;
            }

            memset((VOID *)&ev, 0x0, sizeof(UI_EVENT));
#ifdef SEND_EVENT_TO_RECV_TASK_WITH_PARAM
            event = (Rx_PackDebugTypeDef *)malloc(sizeof(Rx_PackDebugTypeDef));
            if(event == NULL) {
                return ;
            }
            memset((void *)event, 0, sizeof(Rx_PackDebugTypeDef));
            event->packet_type = PACKET_TYPE_ENTIRED;
            event->param1 = swap32(*(uint32_t *)peri);
            event->param2 = len;
            ev.nParam1 = (uint32_t)event;  /// the whole packet
#endif
            ev.nEventId = IO_MSG_UART_RX_PKT;
            if(UOS_SendEvent(bbpro_hci_receive_task_handle, &ev, UOS_NO_WAIT, UOS_EVENT_PRI_URGENT) != true) {
                APP_PRINT_ERROR0("[talkband]bbppro hci receive queue send fail");
            }
        }
    }
    return len;
}
#endif

/**
  * @brief  initialization of pinmux settings and pad settings.
  * @param   No parameter.
  * @return  void
  */
void DataTrans_Board_UART_Init(uint8_t rx_pin_num, uint8_t tx_pin_num)
{
    MODLOGD("DataTrans_Board_UART_Init()\n");
    init_bt_uart();
}

/**
  * @brief  Initializes UART peripheral.
  * @param  None.
  * @retval None.
  */
void DataTrans_Driver_UART_Init(void)
{
    MODLOGD("DataTrans_Driver_UART_Init()\n");

#ifdef DATATRANS_UART_DMA   /// dma
    bt_uart_basic_init(&rtlbt_uart_port);

#if (DATATRANS_UART_BAUDRATE == BAUDRATE_921600)
    bt_uart_change_to_921600(rtlbt_uart_port.port_id);
#elif (DATATRANS_UART_BAUDRATE == BAUDRATE_3000000)
    bt_uart_change_to_3000000(rtlbt_uart_port.port_id);
#endif

    bt_uart_dma_enable(rtlbt_uart_port.port_id, 1);

    bt_uart_irq_init(&rtlbt_uart_port);

    bt_uart_dma_enable(rtlbt_uart_port.port_id, 1);

    /* Register callback function */
#if (AUDIO_PATH_SELECT == AUDIO_PATH_TX_RX_RBUF)
    bt_uart_register_rx_dma_cb(NULL);
#else
    bt_uart_register_rx_dma_cb(DataTrans_UARTRxDmaIntrCallback);
#endif
    DataTrans_RegisterPeriphReadFnCB((WriteQueueFnCB)UART_CopyData);
#else /// fifo
    bt_uart_init(&rtlbt_uart_port);

    rtlbt_uart_port.rx_callback = DataTrans_UARTRxFifoIntrCallback;
    bt_uart_irq_init(&rtlbt_uart_port);

#if (DATATRANS_UART_BAUDRATE == BAUDRATE_921600)
    bt_uart_change_to_921600(rtlbt_uart_port.port_id);
#elif (DATATRANS_UART_BAUDRATE == BAUDRATE_3000000)
    bt_uart_change_to_3000000(rtlbt_uart_port.port_id);
#endif

    /* Register callback function */
    DataTrans_RegisterPeriphReadFnCB((WriteQueueFnCB)UART_ReceiveData);
#endif

}

void DataTrans_Driver_UART_DeInit(void)
{

}

void DataTrans_uart_send(uint8_t * pbuf, uint16_t len)
{
#ifdef DEBUG_UART_TX_RAW_PACKET
    if(pbuf && len) {
        uint16_t i;
        fatal_printf("<<<send packet: ");
        for(i = 0; i < len; i++)
            fatal_printf("%02X ", pbuf[i]);
        fatal_printf("\n");
    }
#endif

    /// fix 3M baudrate send cmd retry error
#if (DATATRANS_UART_BAUDRATE == BAUDRATE_3000000)
    uos_sleep(1);
#endif
    bt_uart_send_data(rtlbt_uart_port.port_id, pbuf, len);
}


#endif/*USE_LV_BLUETOOTH*/

/******************* (C) COPYRIGHT 2017 Realtek Semiconductor Corporation *****END OF FILE****/
